#pragma once
#include "ncti_base.h"
#include <NctiPosition.h>
#include <set>
#include <map>
#include "NctiMeshElement.h"
#include "NctiGridPickSet.h"

namespace NCTI
{
    class NctiDisplayGridMesh;
    class NctiDisplayModelBody;

    /**
    * @brief mesh body for data exchange
    */
    class DECL_NCTI_BASE NctiMeshBody {
    public:
        NctiMeshBody(NctiDisplayGridMesh*);
        ~NctiMeshBody();

        /**
         * @brief auto merge node in mesh.the tolerance is 1e-6
         * @return merged element nodes count
         * */
        Ncti_UInteger MergeNode();


        /**
         * @brief find nodes from area
         * @param[in] i_ptMin area min
         * @param[in] i_ptMax area min
         * @param[in] i_trans trans
         * @param[out] o_pick pick result
         * */
        Ncti_Boolean FindNodesFromArea(const NctiPosition&i_ptMin,const NctiPosition&i_ptMax, std::set<NctiGridPickSet>&o_pick, const NctiTransf&i_trans)const;

        /**
         * @brief find elements from area
         * @param[in] i_ptMin area min
         * @param[in] i_ptMax area min
         * @param[out] o_pick pick result
         * @param[in] i_trans trans
         * */
        Ncti_Boolean FindElementsFromArea(const NctiPosition& i_ptMin, const NctiPosition& i_ptMax, std::set<NctiGridPickSet>& o_pick, const NctiTransf& i_trans)const;

        /**
         * @brief find elements from area
         * @param[in] i_ptMin area min
         * @param[in] i_ptMax area min
         * @param[out] o_pick pick result
         * @param[in] i_trans trans
         * */
        Ncti_Boolean FindFaceFromArea(const NctiPosition& i_ptMin, const NctiPosition& i_ptMax, std::set<NctiGridPickSet>& o_pick, const NctiTransf& i_trans)const;
        
        /**
         * @brief find elements from area
         * @param[in] i_ptMin area min
         * @param[in] i_ptMax area min
         * @param[out] o_pick pick result
         * @param[in] i_trans trans
         * */
        Ncti_Boolean FindEdgeFromArea(const NctiPosition& i_ptMin, const NctiPosition& i_ptMax, std::set<NctiGridPickSet>& o_pick, const NctiTransf& i_trans)const;

        /**
         * @brief find vertexs in the edge direction
         * @param [in] i_elemNumber element number
         * @param [in] i_eindex edge index
         * @param [in] i_deg edge angle in degree
         * @param[out] o_pick pick result
         * */
        Ncti_Boolean FindEdgesFromEdge(Ncti_UInteger i_elemNumber,int i_eindex, Ncti_Float i_deg, std::set<NctiGridPickSet>& o_pick)const;

        /**
         * @brief get fist number info
         * @param[out] startNode the first node number
         * @param[out] startElem the first element number
         * */
        void GetStartNumber(Ncti_UInteger& startNode, Ncti_UInteger& startElem)const;

        /**
         * @brief GetInstanceId
         * */
        const std::pair<int, int>& GetInstanceId()const;

        /**
         * @brief get display mesh object associate with the mesh body
         * @see NctiDisplayGridMesh
         * */
        NctiDisplayGridMesh* GetDispObject()const;

        /**
         * @brief get display geometry object associate with the mesh body
         * @see NctiDisplayModelBody
         * */
        NctiDisplayModelBody* GetModelBody()const;

        /**
         * @brief node count
         * */
        Ncti_UInteger NodeCount()const;

        /**
         * @brief all element count
         * */
        Ncti_UInteger ElemCount()const;

        /**
         * @brief element count statistics info for each element type
         * @see NctiElementType
         * @return type to count pair
         * */
        std::map<NctiElementType, int> ElementInfo()const;

        /**
         * @brief get all Node numbers
         * @details get all numbers vector. ranged in internal order
         * @see AllNodePositions
         * */
        void AllNodeNumber(std::vector<Ncti_UInteger>& o_number)const;

        /**
         * @brief get all Node position
         * @details get all position float vector. ranged in internal order
         * @return node positions.format in node0x,node0y,node0z,node1x...
         * @see AllNodeNumber
         * */
        const std::vector<Ncti_Float>& AllNodePositions()const;

        /**
         * @brief get node position from node number
         * @param [in] i_eid node number
         * @return node position
         * @see NctiPosition
         * */
        NctiPosition GetVertex(Ncti_UInteger i_nid)const;

        /**
         * @brief get all element data by dimension
         * @param [out] o_type element types
         * @param [out] o_data element nodes.
         * @param [in] i_dim dimension.
         *          -1 for all dimension
         *          0 for all vertex element
         *          1 for all edge element
         *          2 for all face element
         *          3 for all volume element
         */
        void AllElementData(std::vector<NctiElementType>&o_type, std::vector<std::vector<Ncti_UInteger>>& o_data, int i_dim)const;

        /**
         * @brief get all element numbers by dimension
         * @param [out] o_number element numbers
         * @param [in] i_dim dimension. 
         *          -1 for all dimension
         *          0 for all vertex element
         *          1 for all edge element
         *          2 for all face element
         *          3 for all volume element
         */
        void AllElementNumber(std::vector<Ncti_UInteger>& o_number,int i_dim)const;

        /**
         * @brief find element contain the node
         * @param [in] i_nodeNumber node number
         * @param [in] i_dim dimension.
         *          -1 for all dimension
         *          0 for all vertex element
         *          1 for all edge element
         *          2 for all face element
         *          3 for all volume element
         * @param [out] o_elementNumber element numbers
         * */
        void FindElementFromNode(Ncti_UInteger i_nodeNumber,std::vector<Ncti_UInteger>&o_elementNumber , int i_dim)const;

        /**
         * @brief get element data from element number
         * @param [in] i_eid element number
         * @return element object
         * @see NctiMeshElement
         * */
        NctiMeshElement GetElement(Ncti_UInteger i_eid)const;

        /**
         * @brief set pick info for highlight
         * @param i_set cells to be pick
         * @param status 0:set,-1:minus,1:add; 
         * @retval:true means changed
         * @retval:false means the same
         * @see NctiGridPickSet
         * */
        bool SetPickDatas(const std::set<NctiGridPickSet>& i_set, int i_status=0);

        /**
         * @brief get pick info
         * @return pick set
         * @see NctiGridPickSet
         * */
        const std::set<NctiGridPickSet>& GetPickDatas()const;

        /**
         * @brief clear pick info
         * */
        void ClearPick();

        /**
         * @brief judge pick info
         * @retval true not empty
         * @retval false empty
         * */
        bool HasAnyPicked()const;

        /**
         * @brief get picked element numbers
         * @param[out] o_elemNumber element numbers picked
         * */
        void GetPickedCells(std::vector<Ncti_UInteger>&o_elemNumber)const;

        /**
         * @brief set startNumber info
         * @warning internal usage. not use it!
         * */
        void SetStartNumber(Ncti_UInteger startNode, Ncti_UInteger startElem);

        /**
         * @brief SetInstanceId
         * @warning internal usage. not use it!
         * */
        void SetInstanceId(const std::pair<int, int>&)const;

        /**
         * @brief SetModelBody
         * @warning internal usage. not use it!
         * */
        void SetModelBody(NctiDisplayModelBody*)const;

        /**
         * @brief init the mesh body
         * @param[in] pos node positions
         * @param[in] type element types
         * @param[in] data element data
         * @warning internal usage ,do not call it!
         * */
        void InitData(const std::vector<Ncti_Float>* pos,
            const std::vector<NctiElementType>* type,
            const std::vector<Ncti_UInteger>* data);

    private:
        struct PrivateData;
        PrivateData* _d;
    };

};

